package cn.wch.meshlib.core;

import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Application;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanFilter;
import android.os.Handler;
import android.os.Looper;

import androidx.annotation.NonNull;
import androidx.annotation.RequiresPermission;

import java.util.List;

import cn.wch.blelib.exception.BLELibException;
import cn.wch.blelib.host.WCHBluetoothManager;
import cn.wch.blelib.host.core.ConnRuler;
import cn.wch.blelib.host.core.Connection;
import cn.wch.blelib.host.core.callback.ConnectCallback;
import cn.wch.blelib.host.core.callback.MTUCallback;
import cn.wch.blelib.host.core.callback.NotifyDataCallback;
import cn.wch.blelib.utils.FormatUtil;
import cn.wch.blelib.utils.LogUtil;
import cn.wch.meshlib.constant.Command;
import cn.wch.meshlib.frame.MeshFrameAssemble;
import cn.wch.meshlib.frame.ack.AckFrame;
import cn.wch.meshlib.frame.ack.CommandAckUtil;

import static java.lang.Thread.sleep;

/**
 * 抽象Mesh控制类
 */
public abstract class AbstractMeshController {
    /**制造商ID（字节数据位低字节在前为：{D7,07}）*/
    private static final int MANUFACTURER_ID =0x07D7;
    /**已配置设备过滤数据*/
    private static final byte[] CONFIGURED={0x01,0x00};
    /**未配置设备过滤数据*/
    private static final byte[] NOT_CONFIGURED={0x00,0x00};
    /**应答接收等待标志*/
    private volatile boolean waitFlag = false;
    /**接收缓冲区*/
    private byte[] buffer;
    /**接收数据长度*/
    private int length = 0;
    /**接收等待超时时长(毫秒)*/
    private long waitTime = 80000;

    private SpecialAckListener ackListener;

    private ConCallback conCallback;
    private String mac;
    private Connection connection=null;

    /**
     * 构造函数
     */
    public AbstractMeshController() {
        buffer = new byte[512];  //初始化接收缓冲区大小
        MeshFrameAssemble.getInstance().setDataListener((data, len) -> {
            LogUtil.d("receive data:" + FormatUtil.bytesToHexString(data, len));
            System.arraycopy(data, 0, buffer, 0, len);//将接收到的源数据复制到buffer缓冲区
            length = len;
            if(intercept()){
                return;
            }
            waitFlag = false;   //将等待标志改为false标明已经收到上位机数据应答
        });
    }

    /**
     * 截取事件
     * @return
     */
    private boolean intercept(){
        //intercept CMD_DELETE_NODE_INFO_ACK/CMD_TRANSFER_RECEIVE
        if(length<=0){
            return true;
        }
        byte[] temp = new byte[length];
        System.arraycopy(buffer, 0, temp, 0, temp.length);
        byte type = temp[0];
        if(type== Command.CMD_DELETE_NODE_INFO_ACK || type==Command.CMD_TRANSFER_RECEIVE){
            AckFrame ackFrame = CommandAckUtil.parseAck(temp);
            if(ackFrame!=null && ackListener!=null){
                ackListener.onChanged(ackFrame);
            }
            return true;
        }
        return false;
    }

    /**关键应答侦听接口*/
    public interface SpecialAckListener{
        /**
         * 有应答改变
         * @param frame 应答帧
         */
        void onChanged(AckFrame frame);
    }
    /**
     * 设置关键应答侦听
     * @param listener
     */
    public void setWatchAckListener(SpecialAckListener listener){
        this.ackListener=listener;
    }
    /**
     * 初始化APP
     * @param application
     * @throws Exception
     */
    protected void initApplication(Application application) throws Exception {
        WCHBluetoothManager.getInstance().init(application);
    }
    /**
     * 写入数据
     * @param characteristic GATT特征信息
     * @param data 写入的数据
     * @param len 数据长度
     * @return
     * @throws Exception
     */
    protected boolean write(BluetoothGattCharacteristic characteristic, byte[] data, int len) throws Exception {
        int write = WCHBluetoothManager.getInstance().write(characteristic, data, len);
        return write == data.length;
    }

    /**
     * 写入并读取数据
     * @param characteristic GATT特征信息
     * @param data 写入的数据
     * @param len 数据长度
     * @return
     * @throws Exception
     */
    protected byte[] writeAndRead(BluetoothGattCharacteristic characteristic, byte[] data, int len) throws Exception {
        waitFlag = true;
        if (!write(characteristic, data, len)) {
            throw new BLELibException("write fail");
        }
        if (!waitIdle(waitTime)) {
            throw new BLELibException("wait timeout");
        }
        byte[] temp = new byte[length];
        System.arraycopy(buffer, 0, temp, 0, temp.length);
        return temp;
    }

    /**
     * 等待直到超时
     * @param waitTime
     * @return
     */
    private boolean waitIdle(long waitTime) {
        long timeout_tmp = waitTime + 1;
        while (timeout_tmp-- > 0) {
            if (!waitFlag) {    //等待标志为false时说明已经收到应答的数据
                LogUtil.d("wait notify success");//等待通知成功
                return true;
            }
            if (!WCHBluetoothManager.getInstance().isConnected()) {
                return false;
            }
            try {
                sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        LogUtil.d("wait notify timeout!");//等待通知超时
        return false;
    }

    /**
     * 扫描设备，查找未配置的设备
     * @param scanCallback
     * @throws Exception
     */
    @SuppressLint("MissingPermission")
    @RequiresPermission(allOf ={Manifest.permission.ACCESS_FINE_LOCATION,Manifest.permission.ACCESS_COARSE_LOCATION} )
    public void scanUnConfiguredBLE(@NonNull ScanCallback scanCallback) throws Exception{
        //no filter
        ScanFilter filter = new ScanFilter.Builder()
                .setManufacturerData(MANUFACTURER_ID,NOT_CONFIGURED)
                .build();
        WCHBluetoothManager.getInstance().startScan(filter, scanCallback);
    }
    /**
     * 扫描设备，查找配置的设备
     * @param scanCallback
     * @throws Exception
     */
    @SuppressLint("MissingPermission")
    @RequiresPermission(allOf ={Manifest.permission.ACCESS_FINE_LOCATION,Manifest.permission.ACCESS_COARSE_LOCATION} )
    public void scanConfiguredBLE(@NonNull ScanCallback scanCallback) throws Exception{
        //no filter
        ScanFilter filter = new ScanFilter.Builder()
                .setManufacturerData(MANUFACTURER_ID,CONFIGURED)
                .build();
        WCHBluetoothManager.getInstance().startScan(filter, scanCallback);
    }
    /**
     * 开始扫描BLE
     * @param isConfigured 已配置的设备
     * @param scanCallback 扫描回调
     * @throws Exception
     */
    @SuppressLint("MissingPermission")
    public void startScan(boolean isConfigured, @NonNull ScanCallback scanCallback)throws Exception{
        ScanFilter filter = new ScanFilter.Builder()
                .setManufacturerData(MANUFACTURER_ID,isConfigured?CONFIGURED:NOT_CONFIGURED)
                .build();
        WCHBluetoothManager.getInstance().startScan(filter, scanCallback);
    }
    /**
     * 停止扫描
     */
    public void stopScan(){
        WCHBluetoothManager.getInstance().stopScan();
    }

    /**
     * 连接设备
     * @param mac mac地址
     * @param conCallback 状态回调
     */
    public void connect(String mac,@NonNull ConCallback conCallback){
        this.conCallback=conCallback;
        this.mac=mac;
//        ConnRuler connRuler=new ConnRuler.Builder(mac).connectTimeout(10000).build();
        try {
            WCHBluetoothManager.getInstance().connect(this.mac, new ConnectCallback() {
                @Override
                public void OnError(String mac, Throwable t) {
                    conCallback.OnError(mac, t);
                }
                @Override
                public void OnConnecting(String mac) {
                    conCallback.OnConnecting(mac);
                }
                @Override
                public void OnConnectSuccess(String mac, Connection connection) {
                    //conCallback.OnConnectSuccess(mac);
                    AbstractMeshController.this.connection=connection;
                }

                @Override
                public void OnDiscoverService(String mac, List<BluetoothGattService> list) {
                    //check characteristic
                    if(checkCharacteristics(list)){
                        preSet(mac);
                        //conCallback.OnConnectSuccess(mac);
                    }else {
                        conCallback.OnInvalidDevice(mac);
                    }
                }

                @Override
                public void OnConnectTimeout(String mac) {
                    conCallback.OnConnectTimeout(mac);
                }

                @Override
                public void OnDisconnect(String mac, BluetoothDevice bluetoothDevice, int status) {
                    conCallback.OnDisconnect(mac, bluetoothDevice, status);
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
            LogUtil.d(e.getMessage());
        }
    }

    /**
     * 检查特性
     * @param list 需要检查的GAT服务列表
     * @return
     */
    abstract boolean checkCharacteristics(List<BluetoothGattService> list);

    /**
     * 预置
     * @param mac
     */
    abstract void preSet(String mac);

    protected void setMTU(){
        try {
            WCHBluetoothManager.getInstance().setMTU(247, (gatt, mtu, status) -> {
                if(status== BluetoothGatt.GATT_SUCCESS){
                    LogUtil.d("set mtu: "+mtu);
                }else {
                    LogUtil.d("set mtu fail");
                }
                //在交换mtu的过程中进行通信是大概率失败的,因此等待mtu交换后完成后再通信
                if(conCallback!=null){
                    conCallback.OnConnectSuccess(mac);
                }
            });
        } catch (BLELibException e) {
            e.printStackTrace();
        }
    }

    protected int getMTU()throws Exception{
        return WCHBluetoothManager.getInstance().getMTU();
    }

    /**
     * 打开特征改变通知。特征改变后会将收到数据写入{@link MeshFrameAssemble}进行处理。
     * 可通过调用{@link MeshFrameAssemble#setDataListener(MeshFrameAssemble.FrameDataListener)}设置侦听器来接收数据。
     * @param characteristic
     */
    protected void openNotify(BluetoothGattCharacteristic characteristic){
        try {
            boolean b = WCHBluetoothManager.getInstance().openNotify(characteristic, new NotifyDataCallback() {
                @Override
                public void OnError(String mac, Throwable t) {
                    LogUtil.d(t.getMessage());
                }

                @Override
                public void OnData(String mac, byte[] data) {
                    MeshFrameAssemble.getInstance().putData(data,data.length);  //写入特征改变通知数据
                }
            });
            if(!b){
                LogUtil.d("open notify fail");
            }
        } catch (BLELibException e) {
            e.printStackTrace();
        }
    }

    /**
     * 设置通知侦听
     * @param characteristic
     * @param notifyDataCallback
     * @return
     */
    protected boolean setNotifyListener( BluetoothGattCharacteristic characteristic,
                                         NotifyDataCallback notifyDataCallback){
        WCHBluetoothManager bleManager=WCHBluetoothManager.getInstance();
        if(bleManager!=null) {
            try {
                if(bleManager.getNotifyState(characteristic)){
                    bleManager.setNotifyListener(characteristic, notifyDataCallback);
                    return true;
                }
            } catch (BLELibException e) {
                e.printStackTrace();
            }
        }
        return false;
    }
    /**
     * 断开连接
     * @param force
     * @throws Exception
     */
    public void disconnect(boolean force)throws Exception{
        WCHBluetoothManager.getInstance().disconnect(force);
    }
    /**检查连接状态*/
    public boolean isConnected(){
        return WCHBluetoothManager.getInstance().isConnected();
    }
}
